#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
新浪财经历史分红数据预处理脚本
功能：读取爬取的分红数据，进行数据预处理，并保存处理后的结果
"""

import logging
import pandas as pd
import numpy as np
from typing import Dict, Any

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("preprocessor.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class DataPreprocessor:
    """数据预处理类"""
    
    def __init__(self, input_file: str):
        """
        初始化数据预处理类
        
        Args:
            input_file: 输入数据文件路径
        """
        self.input_file = input_file
        self.df = None
        self.processed_df = None
    
    def load_data(self) -> bool:
        """
        加载Excel数据
        
        Returns:
            加载成功返回True，否则返回False
        """
        try:
            logger.info(f"正在读取文件: {self.input_file}")
            self.df = pd.read_excel(self.input_file)
            logger.info(f"文件读取成功，数据形状: {self.df.shape}")
            return True
        except Exception as e:
            logger.error(f"读取文件时发生错误: {str(e)}")
            return False
    
    def check_data_shape(self) -> Dict[str, int]:
        """
        检查数据形状
        
        Returns:
            包含行数和列数的字典
        """
        if self.df is None:
            logger.warning("数据未加载")
            return {"rows": 0, "columns": 0}
        
        shape_info = {
            "rows": self.df.shape[0],
            "columns": self.df.shape[1]
        }
        logger.info(f"数据形状: {shape_info}")
        return shape_info
    
    def check_duplicates(self, columns: list = None) -> Dict[str, Any]:
        """
        检查指定列的重复值
        
        Args:
            columns: 要检查的列名列表
            
        Returns:
            包含重复值信息的字典
        """
        if self.df is None:
            logger.warning("数据未加载")
            return {}
        
        if columns is None:
            columns = ['代码', '名称', '详细']
        
        duplicate_info = {}
        
        for col in columns:
            if col in self.df.columns:
                duplicates = self.df[col].duplicated().sum()
                duplicate_info[col] = {
                    "total_count": len(self.df[col]),
                    "unique_count": self.df[col].nunique(),
                    "duplicate_count": duplicates
                }
                logger.info(f"列'{col}'的重复值检查结果: {duplicate_info[col]}")
            else:
                logger.warning(f"列'{col}'不存在于数据中")
                duplicate_info[col] = "列不存在"
        
        return duplicate_info
    
    def show_first_n_rows(self, n: int = 5) -> pd.DataFrame:
        """
        展示数据的前n行
        
        Args:
            n: 要展示的行数
            
        Returns:
            包含前n行数据的DataFrame
        """
        if self.df is None:
            logger.warning("数据未加载")
            return pd.DataFrame()
        
        first_n_rows = self.df.head(n)
        logger.info(f"数据前{n}行:\n{first_n_rows}")
        return first_n_rows
    
    def process_listing_date(self, date_column: str = '上市日期') -> None:
        """
        处理上市日期，提取年、月、日变量
        
        Args:
            date_column: 日期列名
        """
        if self.df is None:
            logger.warning("数据未加载")
            return
        
        if date_column not in self.df.columns:
            logger.warning(f"列'{date_column}'不存在于数据中")
            return
        
        try:
            # 确保日期列是datetime类型
            self.df[date_column] = pd.to_datetime(self.df[date_column], errors='coerce')
            
            # 提取年、月、日
            self.df[f'{date_column}_年'] = self.df[date_column].dt.year
            self.df[f'{date_column}_月'] = self.df[date_column].dt.month
            self.df[f'{date_column}_日'] = self.df[date_column].dt.day
            
            logger.info(f"成功处理'{date_column}'，提取了年、月、日变量")
        except Exception as e:
            logger.error(f"处理日期时发生错误: {str(e)}")
    
    def normalize_numeric_columns(self, numeric_columns: list = None) -> None:
        """
        对数值型变量进行数据归一化处理
        
        Args:
            numeric_columns: 要归一化的数值列列表
        """
        if self.df is None:
            logger.warning("数据未加载")
            return
        
        if numeric_columns is None:
            numeric_columns = ['累计股息(%)', '年均股息(%)', '分红次数', '融资总额(亿元)']
        
        # 复制原始数据
        self.processed_df = self.df.copy()
        
        for col in numeric_columns:
            if col in self.df.columns:
                try:
                    # 将列转换为数值型
                    numeric_data = pd.to_numeric(self.df[col], errors='coerce')
                    
                    # 计算均值和标准差
                    mean_val = numeric_data.mean()
                    std_val = numeric_data.std()
                    
                    # 避免除以零的情况
                    if std_val > 0:
                        # 归一化 (z-score标准化)
                        normalized_data = (numeric_data - mean_val) / std_val
                        self.processed_df[f'{col}_归一化'] = normalized_data
                        logger.info(f"成功对列'{col}'进行归一化处理")
                    else:
                        logger.warning(f"列'{col}'的标准差为0，无法进行归一化")
                except Exception as e:
                    logger.error(f"处理列'{col}'时发生错误: {str(e)}")
            else:
                logger.warning(f"列'{col}'不存在于数据中")
    
    def save_processed_data(self, output_file: str = "新浪财经历史分红数据_预处理.xlsx") -> bool:
        """
        保存预处理后的数据
        
        Args:
            output_file: 输出文件路径
            
        Returns:
            保存成功返回True，否则返回False
        """
        try:
            if self.processed_df is None:
                if self.df is None:
                    logger.warning("没有数据可保存")
                    return False
                else:
                    # 如果没有专门的处理后数据，则使用原始数据
                    self.processed_df = self.df.copy()
            
            # 保存为Excel文件
            self.processed_df.to_excel(output_file, index=False)
            logger.info(f"预处理后的数据已成功保存到：{output_file}")
            return True
        except Exception as e:
            logger.error(f"保存数据时发生错误: {str(e)}")
            return False
    
    def run_full_preprocessing(self, output_file: str = "新浪财经历史分红数据_预处理.xlsx") -> bool:
        """
        运行完整的预处理流程
        
        Args:
            output_file: 输出文件路径
            
        Returns:
            处理成功返回True，否则返回False
        """
        try:
            # 1. 加载数据
            if not self.load_data():
                return False
            
            # 2. 检查数据形状
            self.check_data_shape()
            
            # 3. 检查重复值
            self.check_duplicates()
            
            # 4. 展示前5行数据
            self.show_first_n_rows()
            
            # 5. 处理上市日期
            self.process_listing_date()
            
            # 6. 归一化数值型变量
            self.normalize_numeric_columns()
            
            # 7. 保存预处理后的数据
            return self.save_processed_data(output_file)
            
        except Exception as e:
            logger.error(f"预处理过程中发生错误: {str(e)}")
            return False


if __name__ == "__main__":
    # 输入文件路径
    input_file = "c:\\Users\\YEDX\\task\\第三章作业\\新浪财经历史分红数据.xlsx"
    # 输出文件路径
    output_file = "c:\\Users\\YEDX\\task\\第三章作业\\新浪财经历史分红数据_预处理.xlsx"
    
    # 创建数据预处理实例
    preprocessor = DataPreprocessor(input_file)
    
    # 运行完整的预处理流程
    preprocessor.run_full_preprocessing(output_file)